/**
 * Copyright (c) 2016, biezhi 王爵 (biezhi.me@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.blade.banner;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;

public class BannerFont {
	
	private final int height;
	private final int heightWithoutDescenders;
	private final int maxLine;
	private final int smushMode;
	private final char font[][][] = new char[256][][];
	private final String fontName;

	public static BannerFont load() throws IOException {
		return load("standard.flf");
	}
	
	public static BannerFont load(String fontName) throws IOException {
		return new BannerFont(BannerFont.class.getResource('/' + fontName));
	}
	
	public BannerFont(URL url) throws IOException {
		this(fontLines(url));
	}
	
	private BannerFont(Iterator<String> lines) {
		String dummyS = lines.next();
		StringTokenizer st = new StringTokenizer(dummyS, " ");
		String s = st.nextToken();
		char hardblank = s.charAt(s.length() - 1);
		height = Integer.parseInt(st.nextToken());
		heightWithoutDescenders = Integer.parseInt(st.nextToken());
		maxLine = Integer.parseInt(st.nextToken());
		smushMode = Integer.parseInt(st.nextToken());
		int dummyI = Integer.parseInt(st.nextToken());
		
		st = new StringTokenizer(lines.next(), " ");
		if (st.hasMoreElements())
			fontName = st.nextToken();
		else
			fontName = "";

		for (int i = 0; i < dummyI - 1; i++){
			dummyS = lines.next();
		}
		for (int i = 32; i < 256; i++) {
			for (int h = 0; h < height; h++) {
				dummyS = lines.hasNext() ? lines.next() : null;
				if (dummyS == null){
					i = 256;
				} else {
					// System.out.println(dummyS);
					int iNormal = i;
					boolean abnormal = true;
					if (h == 0) {
						try {
							i = Integer.parseInt(dummyS);
						} catch (NumberFormatException e) {
							abnormal = false;
						}
						if (abnormal) {
							dummyS = lines.next();
						} else {
							i = iNormal;
						}
					}
					if (h == 0)
						font[i] = new char[height][];
					int t = dummyS.length() - 1 - ((h == height - 1) ? 1 : 0);
					if (height == 1)
						t++;
					font[i][h] = new char[t];
					for (int l = 0; l < t; l++) {
						char a = dummyS.charAt(l);
						font[i][h][l] = (a == hardblank) ? ' ' : a;
					}
				}
			}
		}
	}
	
	@SuppressWarnings("deprecation")
	private static Iterator<String> fontLines(URL url) throws IOException {
		InputStream ins = url.openStream();
		DataInputStream dis = new DataInputStream(new BufferedInputStream(ins));
		ArrayList<String> list = new ArrayList<String>();
		String line = null;
		while (true) {
			line = dis.readLine();
			if (line != null) {
				list.add(line);
			} else {
				break;
			}
		}
		dis.close();
		return list.iterator();
	}
	
	public int getHeight() {
		return height;
	}

	public int getMaxLine() {
		return maxLine;
	}

	public int getSmushMode() {
		return smushMode;
	}

	public int getHeightWithoutDescenders() {
		return heightWithoutDescenders;
	}

	public String getFontName() {
		return fontName;
	}

	private String getCharLineString(int c, int l) {
		if (font[c][l] == null){
			return null;
		} else {
			return new String(font[c][l]);
		}
	}

	private static String scroll(String text, int offset) {
		StringBuffer result = new StringBuffer();
		StringBuffer shift = new StringBuffer();
		for (int i = 0; i < offset; i++){
			shift.append(' ');
		}
		StringTokenizer st = new StringTokenizer(text, "\r\n");
		while (st.hasMoreElements()){
			result.append(shift.toString() + st.nextToken() + "\r\n");
		}
		return result.toString();
	}

	private static String addLine(String text, String line, boolean leftJustify, int splitWidth) {
		String result = text;
		if (leftJustify){
			result += line;
		} else {
			result += scroll(line, (splitWidth / 2 - width(line) / 2));
		}
		return result;
	}
	
	public String asAscii(String text) {
		return asAscii(text, false, true, 1024);
	}
	
	public String asAscii(String text, boolean splitAtWord, boolean leftJustify, int splitWidth) {
		String result = "";
		StringTokenizer st = new StringTokenizer(text, " ");
		if (splitAtWord) {
			while (st.hasMoreElements()){
				result = addLine(result, convertOneLine(st.nextToken()), leftJustify, splitWidth);
			}
		} else {
			String line = "";
			while (st.hasMoreElements()) {
				String w = st.nextToken(), word;
				if (line.length() == 0) {
					word = w;
				} else {
					word = ' ' + w;
				}
				String newLine = append(line, word);
				if ((width(newLine) > splitWidth) && (line.length() > 0)) {
					result = addLine(result, line + "\r\n", leftJustify, splitWidth);
					line = append("", w);
				} else{
					line = newLine;
				}
			}
			if (line.length() > 0){
				result = addLine(result, line + "\r\n", leftJustify, splitWidth);
			}
		}
		return result;
	}

	private static int width(String text) {
		int w = 0;
		StringTokenizer st = new StringTokenizer(text, "\r\n");
		while (st.hasMoreElements())
			w = Math.max(w, st.nextToken().length());
		return w;
	}
	
	private String convertOneLine(String text) {
		String result = "";
		for (int l = 0; l < height; l++) { // for each line
			for (int c = 0; c < text.length(); c++) // for each char
				result += getCharLineString((int) text.charAt(c), l);
			result += "\r\n";
		}
		return result;
	}

	private String append(String text, String end) {
		StringBuffer result = new StringBuffer();
		int h = 0;
		if (text.length() == 0){
			for (int i = 0; i < height; i++){
				text += " \r\n";
			}
		}
		StringTokenizer st = new StringTokenizer(text, "\r\n");
		int count = st.countTokens();
		while (st.hasMoreElements()) {
			result.append(st.nextToken());
			for (int c = 0; c < end.length(); c++){
				result.append(getCharLineString((int) end.charAt(c), h));
			}
			if(h < (count - 2)){
				result.append("\r\n");
			}
			h++;
		}
		return result.toString();
	}
}
